package com.enjoyor.soa.traffic.frame.support.mybatis.mapper.impl;

import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.enjoyor.soa.traffic.frame.constant.BaseSqlId;
import com.enjoyor.soa.traffic.frame.support.mybatis.mapper.IBaseMapper;
import com.enjoyor.soa.traffic.frame.support.mybatis.page.MyBatisSql;
import com.enjoyor.soa.traffic.frame.support.mybatis.page.MyBatisSqlUtils;
import com.enjoyor.soa.traffic.util.base.BeanUtil;
import com.enjoyor.soa.traffic.util.base.UUIDUtil;
import com.enjoyor.soa.traffic.util.bean.base.ABean;
import com.enjoyor.soa.traffic.util.exception.ManagerException;
import com.enjoyor.soa.traffic.util.pojo.Page;

@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
public abstract class BaseMapper<T extends ABean> implements IBaseMapper<T> {

    @Autowired(required = true)
    protected SqlSession sqlSessionTemplate;

    @Autowired
    protected SqlSessionFactory sqlSessionFactory;

    public static final String SQLNAME_SEPARATOR = ".";

    private String sqlNamespace = getDefaultSqlNamespace();

    protected String getDefaultSqlNamespace() {
        Class<?> genericClass = BeanUtil.getGenericClass(getClass());
        return genericClass == null ? null : genericClass.getName();
    }

    public String getSqlNamespace() {
        return this.sqlNamespace;
    }

    public void setSqlNamespace(String sqlNamespace) {
        this.sqlNamespace = sqlNamespace;
    }

    public String getSqlName(String sqlName) {
        return this.sqlNamespace + "." + sqlName;
    }

    public SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    protected String generateId() {
        return UUIDUtil.random();
    }

    public T selectById(String id) {
        Assert.notNull(id);
        try {
            return (T) this.sqlSessionTemplate.selectOne(getSqlName(BaseSqlId.SQL_SELECT_BY_ID), id);
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("根据ID查询对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_SELECT_BY_ID)}), e);
        }
    }

    public List<T> selectList(T entity) {
        try {
            return this.sqlSessionTemplate.selectList(getSqlName(BaseSqlId.SQL_SELECT), entity);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ManagerException(
                    String.format("查询对象列表出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_SELECT)}), e);
        }
    }

    public List<T> selectAll() {
        try {
            return this.sqlSessionTemplate.selectList(getSqlName(BaseSqlId.SQL_SELECT));
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("查询所有对象列表出错！语句%s", new Object[]{getSqlName(BaseSqlId.SQL_SELECT)}), e);
        }
    }

    public Integer selectCount() {
        try {
            return (Integer) this.sqlSessionTemplate.selectOne(getSqlName(BaseSqlId.SQL_SELECT_COUNT));
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("查询对象总数出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_SELECT_COUNT)}), e);
        }
    }

    public void insert(T entity) {
        Assert.notNull(entity);
        try {
            if (StringUtils.isBlank(entity.getId())) {
                entity.setId(String.valueOf(System.nanoTime()));
            }
            this.sqlSessionTemplate.insert(getSqlName(BaseSqlId.SQL_INSERT), entity);
        } catch (Exception e) {
            throw new ManagerException(String.format("添加对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_INSERT)}),
                    e);
        }
    }

    @Override
    public void insertSelective(T paramT) {
        Assert.notNull(paramT);
        try {
            if (StringUtils.isBlank(paramT.getId())) {
                paramT.setId(String.valueOf(System.nanoTime()));
            }
            this.sqlSessionTemplate.insert(getSqlName(BaseSqlId.SQL_INSERT_SELECTIVE), paramT);
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("添加对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_INSERT_SELECTIVE)}), e);
        }

    }

    public void insertInBatch(List<T> entityList) {
        if ((entityList == null) || (entityList.isEmpty())) {
            return;
        }
        for (T entity : entityList) {
            insert(entity);
        }
    }

    public int updateById(T entity) {
        Assert.notNull(entity);
        try {
            return this.sqlSessionTemplate.update(getSqlName(BaseSqlId.SQL_UPDATE_BY_ID), entity);
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("根据ID更新对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_UPDATE_BY_ID)}), e);
        }
    }

    public int updateByIdSelective(T entity) {
        Assert.notNull(entity);
        try {
            return this.sqlSessionTemplate.update(getSqlName(BaseSqlId.SQL_UPDATE_BY_ID_SELECTIVE), entity);
        } catch (Exception e) {
            throw new ManagerException(String.format("根据对象更新某些属性出错！语句：%s",
                    new Object[]{getSqlName(BaseSqlId.SQL_UPDATE_BY_ID_SELECTIVE)}), e);
        }
    }

    public void updateInBatch(List<T> entityList) {
        if ((entityList == null) || (entityList.isEmpty())) {
            return;
        }
        for (T entity : entityList) {
            updateById(entity);
        }
    }

    public void updateInBatchSelective(List<T> entityList) {
        if ((entityList == null) || (entityList.isEmpty())) {
            return;
        }
        for (T entity : entityList) {
            updateByIdSelective(entity);
        }
    }

    public int deleteById(String id) {
        Assert.notNull(id);
        try {
            return this.sqlSessionTemplate.delete(getSqlName(BaseSqlId.SQL_DELETE_BY_ID), id);
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("根据ID删除对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_DELETE_BY_ID)}), e);
        }
    }

    public int delete(T entity) {
        Assert.notNull(entity);
        try {
            return this.sqlSessionTemplate.delete(getSqlName(BaseSqlId.SQL_DELETE), entity);
        } catch (Exception e) {
            throw new ManagerException(String.format("删除对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_DELETE)}),
                    e);
        }
    }

    public int deleteAll() {
        try {
            return this.sqlSessionTemplate.delete(getSqlName(BaseSqlId.SQL_DELETE));
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("删除所有对象出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_DELETE)}), e);
        }
    }

    public void deleteByIdInBatch(String[] idList) {
        if ((idList == null) || (idList.length == 0)) {
            return;
        }
        for (String id : idList) {
            deleteById(id);
        }
    }

    public boolean checkFieldExists(String type, String value, String id) {
        Map<String, String> condtion = new HashMap();
        boolean result = false;
        try {
            condtion.put("type", type);
            condtion.put("value", value);
            condtion.put("id", id);
            int count = ((Integer) this.sqlSessionTemplate.selectOne(getSqlName(BaseSqlId.SQL_EXISTS), condtion))
                    .intValue();
            if (count == 0) {
                result = true;
            }
        } catch (Exception e) {
            throw new ManagerException(
                    String.format("检查字段重复性出错！语句：%s", new Object[]{getSqlName(BaseSqlId.SQL_EXISTS)}), e);
        }
        return result;
    }

    @Override
    public Page selectPageInfo(Page page) {
        return this.selectPageInfo(getSqlName(BaseSqlId.SQL_SELECT_PAGE), page);
    }

    @Override
    public Page selectPageInfo(String sqlId, Page page) {
        Map<String, Object> countParam = new HashMap<String, Object>();
        Map<String, Object> mapBean = page.getMapBean();
        if (mapBean != null) {
            for (Map.Entry<String, Object> entry : mapBean.entrySet()) {
                countParam.put(entry.getKey(), entry.getValue());
            }
        } else
            mapBean = new HashMap<String, Object>();
        countParam.put("PCOUNT", "PAGECOUNT");
        int totalCount = selectTotalCount(sqlId, countParam);
        if (page.getSortExp() != null && page.getSortExp().compareTo("") != 0) {
            mapBean.put("_sidx", page.getSortExp());
            mapBean.put("_sord", page.getSortDir());
        }
        page.setTotalCount(totalCount);
        RowBounds rowBounds = new RowBounds(page.getCurrentResult(), page.getPageSize());
        try {
            List list = sqlSessionTemplate.selectList(sqlId, mapBean, rowBounds);
            page.setResult(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return page;
    }

    @Override
    public int selectTotalCount(String sqlId, Map<String, Object> map) {
        MyBatisSql myBatisSql = MyBatisSqlUtils.getMyBatisSql(sqlId, map, sqlSessionFactory);
        String sql = myBatisSql.getSql();
        int count = 0;
        Connection conn = null;
        PreparedStatement countStmt = null;
        ResultSet rs = null;
        try {

            /*
             * if (conn instanceof DruidPooledConnection) { // conn = //
             * sqlSessionTemplate.getConfiguration().getEnvironment().
             * getDataSource().getConnection(); SqlSessionTemplate st =
             * (SqlSessionTemplate) sqlSessionTemplate; conn =
             * SqlSessionUtils.getSqlSession(st.getSqlSessionFactory(),
             * st.getExecutorType(),
             * st.getPersistenceExceptionTranslator()).getConnection(); } else
             * conn = sqlSessionTemplate.getConnection();
             */
            conn = sqlSessionTemplate.getConfiguration().getEnvironment().getDataSource().getConnection();
            countStmt = conn.prepareStatement(buildCountSql(sql));
            List<Object> parametersArray = Arrays.asList(myBatisSql.getParameters());
            List<Object> list = new ArrayList<Object>(parametersArray);
            if (sql.indexOf("?") != -1 && list.size() > 0 && myBatisSql.getParameters().length > 0) {
                for (int i = 0; i < list.size(); i++) {
                    if (list.get(i) instanceof String) {
                        countStmt.setString(i + 1, list.get(i).toString());
                    } else if (list.get(i) instanceof java.util.Date) {
                        countStmt.setTimestamp(i + 1, new java.sql.Timestamp(((Date) list.get(i)).getTime()));
                    } else if (list.get(i) instanceof Integer) {
                        countStmt.setInt(i + 1, (Integer) list.get(i));
                    } else if (list.get(i) instanceof Long) {
                        countStmt.setLong(i + 1, (Long) list.get(i));
                    } else if (list.get(i) instanceof Float) {
                        countStmt.setFloat(i + 1, (Float) list.get(i));
                    } else if (list.get(i) instanceof Double) {
                        countStmt.setDouble(i + 1, (Double) list.get(i));
                    } else if (list.get(i).getClass().isArray()) {
                        countStmt.setArray(i + 1, (Array) list.get(i));
                    } else {
                        countStmt.setString(i + 1, list.get(i).toString());
                    }
                }
            }
            rs = countStmt.executeQuery();
            if (rs.next())
                count = rs.getInt(1);
        } catch (SQLException e) {
            System.out.println("SQLException:" + e.getClass() + e.getMessage());
        } finally {
            try {
                if (rs != null)
                    rs.close();
                if (countStmt != null)
                    countStmt.close();
                if (conn != null)
                    conn.close();
            } catch (SQLException e) {
                System.out.println("SQLException:" + e.getClass() + e.getMessage());
            }
        }
        return count;
    }

    public String buildCountSql(String sql) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("select count(1) from ( ");
        buffer.append(sql);
        buffer.append(" ) tmp_count ");
        return buffer.toString();
    }

    @Override
    public Page selectPageInfo(String sqlId, String countSqlId, Page page) {
        Map<String, Object> mapBean = page.getMapBean();
        if (mapBean == null)
            mapBean = new HashMap<String, Object>();
        int totalCount = selectTotalCount(countSqlId, mapBean);
        if (page.getSortExp() != null && page.getSortExp().compareTo("") != 0) {
            mapBean.put("_sidx", page.getSortExp());
            mapBean.put("_sord", page.getSortDir());
        }
        page.setTotalCount(totalCount);
        RowBounds rowBounds = new RowBounds(page.getCurrentResult(), page.getPageSize());
        List list = sqlSessionTemplate.selectList(sqlId, mapBean, rowBounds);
        page.setResult(list);
        return page;
    }

}
